var _global = require('global/global.js');
var _UIContainer = require('component/uicontainer.js');
var _inherit = require('core/inherit.js');
var _TouchNode = require('event/touchnode.js').TouchNode;
var _vec3f = require('core/glm.js').vec3f;

var _ButtonIds = require('event/buttonid.js');
var BUTTON_DOWN = _ButtonIds.BUTTON_DOWN;
var BUTTON_UP = _ButtonIds.BUTTON_UP;
var BUTTON_MOVE = _ButtonIds.BUTTON_MOVE;
var BUTTON_CANCEL = _ButtonIds.BUTTON_CANCEL;

var _Constant = require('scenes/game/constant.js');
var _UIUtils = require('scenes/game/utils.js');

var WIDTH = _Constant.areaSize[0];
var HEIGHT = _Constant.areaSize[1];
var UNIT_WIDTH = _Constant.slotSize[0];
var UNIT_HEIGHT = _Constant.slotSize[1];

var STATUS_WAITING = 0;
var STATUS_MOVEING = 1;
var STATUS_ABSORBS = 2;

var DIRECTION_NONE = -1;
var DIRECTION_ALL = 4;

var _Cell = require('scenes/game/cell.js');

// ==========================
// Slot
// ==========================
var MOVE_LEFT = 0;
var MOVE_TOP = 1;
var MOVE_RIGHT = 2;
var MOVE_BOTTOM = 3;

var ROW_FIRST = [MOVE_LEFT, MOVE_RIGHT, MOVE_TOP, MOVE_BOTTOM];
var COLUMN_FIRST = [MOVE_TOP, MOVE_BOTTOM, MOVE_LEFT, MOVE_RIGHT];

var INIT_CELLS = 9;
var CELLS_COUNT = _Constant.columns * _Constant.rows;

function clip(v, min, max) {
    return v < min ? min : v > max ? max : v;
}

// ==========================
// GameArea
// ==========================
function SlideParam(vindex, min, sig, cmin, cmax) {
    this.mVindex = vindex;
    this.mMin = min;
    this.mSig = sig;
    this.mCmin = cmin;
    this.mCmax = cmax;
}
SlideParam.prototype.matches = function (offset) {
    return offset[this.mVindex] * this.mSig > this.mMin;
};
SlideParam.prototype.offset = function (vector, move) {
    var offset = this.mSig * this.mMin;
    vector[this.mVindex] += offset;
    move[this.mVindex] -= offset;
};
SlideParam.prototype.clip = function (move) {
    move[this.mVindex] = clip(move[this.mVindex], this.mCmin, this.mCmax);
    move[(this.mVindex + 1) % 2] = 0;
};
SlideParam.prototype.getIndex = function () {
    return this.mVindex;
};

// ==========================
// TouchArea
// ==========================
function TouchArea() {
    this.mAreaBegin = new _vec3f();
    this.mAreaSize = new _vec3f();
    this.mTouchVector = new _vec3f();
}
TouchArea.prototype.setSize = function (left, top, right, bottom) {
    this.mAreaBegin.set(-left, -bottom);
    this.mAreaSize.set(left + right + WIDTH, top + bottom + HEIGHT);
};
TouchArea.prototype.isInArea = function (vector) {
    var v = this.mTouchVector;
    vector.sub(this.mAreaBegin, v);
    if (v[0] < 0 || v[1] < 0) {
        return false;
    }
    v.sub(this.mAreaSize);
    if (v[0] > 0 || v[1] > 0) {
        return false;
    }
    return true;
};

// ==========================
// GameArea
// ==========================
var _Target = require('./targetaction.js');
var _Animation = require('./slideanima.js');

function GameArea() {
    _UIContainer.call(this);
    _Target.onCreate(this);
    this.setId('gamearea');
    this.mLocal = _Constant.getLocal();

    this.mTitleView = module.$('./fragments/title.js');
    this.mTitleView.setId('game.title');
    this.mLocal.add('ui.title', this.mTitleView);

    if (!this.mLocal.find('istoturial')) {
        this.mLocal.find('ui.header').addFragment(this.mTitleView);
    }

    this.mStatus = STATUS_WAITING;
    this.mResetTouch = false;

    // touch predict
    this.mDirection = DIRECTION_NONE;
    this.mPredict = MOVE_LEFT;

    // touch area
    this.mDownVector = new _vec3f();
    this.mMoveVector = new _vec3f();
    this.mTouchArea = new TouchArea();
    this.mSlideParams = new Array(DIRECTION_ALL);
    this.mSlideParams[MOVE_LEFT] = new SlideParam(0, 8, -1, -UNIT_WIDTH, 0);
    this.mSlideParams[MOVE_RIGHT] = new SlideParam(0, 8, 1, 0, UNIT_WIDTH);
    this.mSlideParams[MOVE_TOP] = new SlideParam(1, 8, 1, 0, UNIT_HEIGHT);
    this.mSlideParams[MOVE_BOTTOM] = new SlideParam(1, 8, -1, -UNIT_HEIGHT, 0);

    // cells
    this.mCellPool = [];
    this.mCells = module.$('./cellgroup.js');
    this.mCellRandom = module.$('./slotgenerate.js');
    this.mNextCell = 0;

    // animations
    this.mAbsorbs = new _Animation(this);

    this.mBg = _UIUtils.createGridBg(_Constant.rows, _Constant.columns);
    this.mAnimaLayer = module.$('component/container.js');
    this.mAnimaLayer.setSize(_Constant.areaSize);

    this.setSize(_Constant.areaSize);
}
_inherit(GameArea, _UIContainer);
_Target.asAction(GameArea);

GameArea.prototype.getCellSlots = function () {
    return this.mCells.getAllSlots();
};
GameArea.prototype.getAnimaLayer = function () {
    return this.mAnimaLayer;
};
GameArea.prototype.initGame = function () {
    // init cells
    var initcells = [1, 1, 1, 2, 2, 2, 3, 3, 3];
    if (!this.mLocal.find('istoturial')) {
        var all = [];
        for (var i = 0; i < CELLS_COUNT; i++) {
            all.push(i);
        }
        for (var c = 0; c < INIT_CELLS; c++) {
            var index = Math.floor(Math.random() * all.length);
            var slot = this.mCells.getSlot(all[index]);
            all.splice(index, 1);
            //slot.bindCell(this.createCell(this.createNextCell()));
            slot.bindCell(this.createCell(initcells.pop()));
        }
    }

//    // test code
//    this.mCells.loadCellsCount([
//        1, 12, 3, 2,
//        1, 3, 2, 2,
//        12, 6, 192, 384,
//        6144, 3072, 1536, 768
//    ]);
//    // test code
//    this.mCells.loadCellsCount([
//        1, 12, 3, 2,
//        0, 0, 2, 2,
//        0, 0, 0, 0,
//        3072, 3072, 3072, 3072
//    ]);
//    // test code
//    this.mCells.loadCellsCount([
//        3, 24, 1, 3,
//        768, 6, 96, 384,
//        3, 48, 3, 0,
//        2, 12, 0, 0
//    ]);

    this.mCells.prepareMove();// prepare for the first move
    this.prepareNext();

//    // TODO testcode
//    _global.scheduleTask(function () {
//        this.mTitleView.hideAll();
//        this.fire('gameover', this.mCells);
//    }, this);
};
/**
 * @returns {*}
 */
GameArea.prototype.getCellsCount = function () {
    return this.mCells.getCellsCount();
};
GameArea.prototype.recoverFromLostContext = function (context) {
    this.mBg.recoverFromLostContext(context);
    this.mCells.recoverFromLostContext(context);
    this.mAnimaLayer.recoverFromLostContext(context);
    // recover cell pool
    for (var i = 0; i < this.mCellPool.length; i++) {
        this.mCellPool[i].recoverFromLostContext(context);
    }
    this.mTitleView.recoverFromLostContext(context);
};
GameArea.prototype.drawContent = function (context) {
    this.mBg.draw(context);
    this.mCells.draw(context);

    if (this.mStatus != STATUS_WAITING) {
        this.mCells.drawMoing(this.mPredict, context);
    }

    this.mAnimaLayer.draw(context);
};
GameArea.prototype.getTitleView = function () {
    return this.mTitleView;
};

// ================ create new slot
/**
 * init cells
 */
GameArea.prototype.createNextCell = function () {
    this.mCellRandom.updateMax(this.mCells.getMaxSlot());
    return this.mCellRandom.randomCell();
};
GameArea.prototype.prepareNext = function () {
    this.mNextCell = this.createNextCell();
    this.mTitleView.onNextCell(this.mNextCell);
};
/**
 * new slot slide in
 */
GameArea.prototype.findSlideInSlot = function () {
    // update merge predict
    this.mCells.predictMerge(this.mPredict);

    // find history merge
    var his = this.mCells.getMergeHis();
    var current = his.getCurrent();
    var hashis = false;
    if (current.getMergeCount() > 0 || (current = his.getPrevious()).getMergeCount() > 0) {
        hashis = true;
    }

    // update random privilege
    var snap = this.mCells.getMoveSnap(this.mPredict);
    var lines;
    if (hashis) {
        lines = snap.getPrivilegedData(current.getLines(this.mPredict));
    } else {
        lines = snap.getData();
    }

    var l;
    if ((l = lines.mergeCount()) > 0) {
        l = Math.floor(Math.random() * l);
        l = lines.getMergeIndex(l);
    } else {
        l = lines.emptyCount();
        l = Math.floor(Math.random() * l);
        l = lines.getEmptyIndex(l);
    }

    return this.mCells.getLineEnd(this.mPredict, l);
};

// ---------------- pool
/**
 * cell pool
 * @param count
 * @returns {_Cell}
 */
GameArea.prototype.releaseCell = function (cell) {
    this.mCellPool.push(cell.onRelease());
};
GameArea.prototype.createCell = function (count) {
    if (this.mCellPool.length) {
        return this.mCellPool.pop().onInit(count);
    }
    return new _Cell(count);
};

// ================ animation callback
/**
 * called when finger leave screen
 * @param direction
 * @param vec
 */
GameArea.prototype.startAbsorb = function (direction, vec) {
    var hasnext = this.mAbsorbs.initMove(direction, vec);
    if (hasnext) {
        this.mAbsorbs.start(this.findSlideInSlot());
    } else {
        this.mAbsorbs.start();
    }
};
GameArea.prototype.onAbsorb = function (direction, vec) {
    this.mCells.move(direction, vec);
};
/**
 * called when an cancel absorb is finished
 * @param merge
 */
GameArea.prototype.onAbsorbCancel = function () {
    this.waitingForTouch();
};
/**
 * @param hasslide if new cell will slide in
 */
GameArea.prototype.onAbsorbFinished = function (hasslide) {
    this.mCells.finishMerge(this.mPredict, this);
    if (!hasslide) {
        this.mCells.prepareMove();// if no slidein, merge is the only action
        this.waitingForTouch();
    }
};
/**
 * crate next cell on given slot
 * @param slot
 */
GameArea.prototype.onSlideinStart = function (slot) {
    var cell = this.createCell(this.mNextCell);
    slot.bindCell(cell);

    // check if there is possiable movement, prepare for the next slide
    this.mCells.prepareMove();

    if (this.mCells.isNoMove()) {
        this.mTitleView.hideAll();
        this.fire('gameover', this.mCells);
    } else {
        this.prepareNext();
    }
};
GameArea.prototype.onSlideinFinish = function () {
    this.waitingForTouch();
};
/**
 * returun current move direction
 * @returns {*}
 */
GameArea.prototype.getPredict = function () {
    return this.mPredict;
}
GameArea.prototype.resetTouch = function () {
    this.mResetTouch = true;
};
GameArea.prototype.waitingForTouch = function () {
    this.mStatus = STATUS_WAITING;
    this.mDirection = DIRECTION_NONE;
    this.mResetTouch = true;
};

// ================ touch process
GameArea.prototype.createEventNode = function () {
    return new _TouchNode(this);
};
GameArea.prototype.setTouchMargin = function (left, top, right, bottom) {
    this.mTouchArea.setSize(left, top - 163, right, bottom);
    this.mAbsorbs.setMargin(left, top, right, bottom);
};
GameArea.prototype.startMove = function (vector) {
    this.mDownVector.set(vector);
    this.mStatus = STATUS_MOVEING;
    this.mDirection = DIRECTION_NONE;
};
GameArea.prototype.keepMoving = function (vector) {
    vector.sub(this.mDownVector, this.mMoveVector);

    // get direction
    if (this.mDirection == DIRECTION_NONE) {
        var order = Math.abs(this.mMoveVector[0]) > Math.abs(this.mMoveVector[1]) ? ROW_FIRST : COLUMN_FIRST;
        for (var i = 0; i < DIRECTION_ALL; i++) {
            var o = order[i];
            if (this.mSlideParams[o].matches(this.mMoveVector)) {
                this.mSlideParams[o].offset(this.mDownVector, this.mMoveVector);
                this.mDirection = o;
                break;
            }
        }
        if (this.mDirection == DIRECTION_NONE) {
            return;
        }
    }

    this.mPredict = this.mDirection;
    this.mSlideParams[this.mDirection].clip(this.mMoveVector);
    this.mCells.move(this.mPredict, this.mMoveVector);
};
GameArea.prototype.endMove = function (vector) {
    this.keepMoving(vector);

    // if there is no movement, return at once
    if (this.mCells.isBlock(this.mPredict)) {
        this.mStatus = STATUS_WAITING;
        return;
    }

    this.startAbsorb(this.mPredict, this.mMoveVector);
    this.mStatus = STATUS_ABSORBS;
    this.mDirection = DIRECTION_NONE;// reset direction
};
GameArea.prototype.onTouch = function (event) {
    if (!this.mTouchArea.isInArea(event.vector)) {
        (this.mStatus == STATUS_MOVEING) && this.endMove(event.vector);// has already moved
        return false;
    }
    if (this.mStatus == STATUS_ABSORBS) {
        return true;
    }

    // reset down vector
    if (this.mResetTouch) {
        if (event.state != BUTTON_DOWN) {
            this.startMove(event.vector);
        }
        this.mResetTouch = false;
    }

    // begin touch
    switch (event.state) {
        case BUTTON_DOWN:
            this.startMove(event.vector);
            break;

        case BUTTON_UP:
        case BUTTON_CANCEL:
            this.endMove(event.vector);
            break;

        case BUTTON_MOVE:
            this.keepMoving(event.vector);
            break;
    }
    return true;
};

module.exports = GameArea;